<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="Jtkqx" id="Jtkqx"><span data-lake-id="u83a6389b" id="u83a6389b">典型回答</span></h1>
  <p data-lake-id="u8fc34360" id="u8fc34360"><br></p>
  <p data-lake-id="ube3dc23d" id="ube3dc23d"><span data-lake-id="u247934cc" id="u247934cc">@Async中关于线程池的使用部分在AsyncExecutionInterceptor中，在这个类中有一个getDefaultExecutor方法， 当我们没有做过自定义线程池的时候，就会用SimpleAsyncTaskExecutor这个线程池。</span></p>
  <p data-lake-id="u64961212" id="u64961212"><span data-lake-id="uc0820877" id="uc0820877"></span></p>
  <pre lang="java"><code>
@Override
protected Executor getDefaultExecutor(BeanFactory beanFactory) {
    Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
    return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}
</code></pre>
  <p data-lake-id="uf7e3b549" id="uf7e3b549"><span data-lake-id="u0f55b5e3" id="u0f55b5e3"></span></p>
  <blockquote data-lake-id="u14119823" id="u14119823">
   <p data-lake-id="u28a45a1b" id="u28a45a1b"><span data-lake-id="ucb40d34c" id="ucb40d34c">但是这里需要注意的是，他并不是无脑的的直接创建一个新的，这部分在扩展知识中讲</span></p>
  </blockquote>
  <p data-lake-id="u4bd88f25" id="u4bd88f25"><span data-lake-id="u6da30fd6" id="u6da30fd6">​</span><br></p>
  <p data-lake-id="u0dfac0f5" id="u0dfac0f5"><span data-lake-id="ue16a0df4" id="ue16a0df4">SimpleAsyncTaskExecutor这玩意坑很大，其实他并不是真的线程池，它是不会重用线程的，每次调用都会创建一个新的线程，也没有最大线程数设置。并发大的时候会产生严重的性能问题。</span></p>
  <p data-lake-id="u4b579e8d" id="u4b579e8d"><span data-lake-id="uaccf028a" id="uaccf028a">​</span><br></p>
  <p data-lake-id="u653c20e6" id="u653c20e6"><span data-lake-id="ud2ee9939" id="ud2ee9939">他的doExecute核心逻辑如下：<br><br></span></p>
  <pre lang="java"><code>

	/**
	 * Template method for the actual execution of a task.
	 * &lt;p&gt;The default implementation creates a new Thread and starts it.
	 * @param task the Runnable to execute
	 * @see #setThreadFactory
	 * @see #createThread
	 * @see java.lang.Thread#start()
	 */
	protected void doExecute(Runnable task) {
		Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
		thread.start();
	}

</code></pre>
  <p data-lake-id="u63d1077a" id="u63d1077a"><br></p>
  <p data-lake-id="u0ec2f2c3" id="u0ec2f2c3"><span data-lake-id="u73158b91" id="u73158b91">所以，我们应该自定义线程池来配合@Async使用，而不是直接就用默认的。</span></p>
  <p data-lake-id="u62d6c85c" id="u62d6c85c"><span data-lake-id="u5c123764" id="u5c123764">​</span><br></p>
  <h1 data-lake-id="pLn6j" id="pLn6j"><span data-lake-id="u62042f1b" id="u62042f1b">扩展知识</span></h1>
  <p data-lake-id="ufce929d0" id="ufce929d0"><br></p>
  <h2 data-lake-id="rkp45" id="rkp45"><span data-lake-id="uaad183e6" id="uaad183e6">自定义线程池</span></h2>
  <p data-lake-id="ub0983712" id="ub0983712"><br></p>
  <p data-lake-id="u5c54497e" id="u5c54497e"><span data-lake-id="u7a55b0ff" id="u7a55b0ff">我们可以通过如下方式，自定义一个线程池：</span></p>
  <p data-lake-id="u9ec9b635" id="u9ec9b635"><span data-lake-id="u410110ae" id="u410110ae">​</span><br></p>
  <p data-lake-id="u089399b3" id="u089399b3"><span data-lake-id="u6f6230cd" id="u6f6230cd">例子用的是这篇的改造的：</span></p>
  <p data-lake-id="u7e1d4582" id="u7e1d4582"><span data-lake-id="u20c73582" id="u20c73582">​</span><br></p>
  <pre lang="java"><code>
@Configuration
@EnableAsync
public class AsyncExecutorConfig {
    @Bean("registerSuccessExecutor")
    public Executor caseStartFinishExecutor() {

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("registerSuccessExecutor-%d").build();

        ExecutorService executorService = new ThreadPoolExecutor(100, 200,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue&lt;Runnable&gt;(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

        return executorService;
    }

}

</code></pre>
  <p data-lake-id="ud97af999" id="ud97af999"><br></p>
  <p data-lake-id="u7235a6e1" id="u7235a6e1"><span data-lake-id="u1cd929b3" id="u1cd929b3">并且把他声明为@Configuration，然后也可以把Application.java中的 @EnableAsync放到这里。</span></p>
  <p data-lake-id="uf4ba11ac" id="uf4ba11ac"><span data-lake-id="ue75a450f" id="ue75a450f">​</span><br></p>
  <p data-lake-id="u52f07b14" id="u52f07b14"><span data-lake-id="u01287f3d" id="u01287f3d">接下来在使用@Async的时候，指定一下即可：</span></p>
  <p data-lake-id="uc9e16b0f" id="uc9e16b0f"><span data-lake-id="ude445f79" id="ude445f79">​</span><br></p>
  <pre lang="java"><code>
/**
 * 案件中心内部事件监听器
 *
 * @author Hollis
 */
@Component
public class RegisterEventListener {

    @EventListener(RegisterSuccessEvent.class)
     @Async("registerSuccessExecutor")
    public void onApplicationEvent(RegisterSuccessEvent event) {
        RegisterInfo registerInfo = (RegisterInfo) event.getSource();

        //执行发送欢迎短信的逻辑
        //这里注意要控制好并发，这个例子就不细说了，可以参考：✅基于Redis的分布式锁，解决短信验证码重复发放等问题
    }
}

</code></pre>
  <p data-lake-id="ud8eef05e" id="ud8eef05e"><br></p>
  <p data-lake-id="u06caefb1" id="u06caefb1"><span data-lake-id="u058cf6b6" id="u058cf6b6">在@Async中指定registerSuccessExecutor即可。这样在后续执行时，就会用到我们自定义的线程池了。</span></p>
  <p data-lake-id="u6848783b" id="u6848783b"><span data-lake-id="udec0b7c3" id="udec0b7c3">​</span><br></p>
  <p data-lake-id="uc5fa0919" id="uc5fa0919"><span data-lake-id="u677a1d91" id="u677a1d91">​</span><br></p>
  <h2 data-lake-id="JTAdb" id="JTAdb"><span data-lake-id="udba606a0" id="udba606a0">不是无脑创建SimpleAsyncTaskExecutor</span></h2>
  <p data-lake-id="u5e020182" id="u5e020182"><br></p>
  <p data-lake-id="u8afc1fc0" id="u8afc1fc0"><span data-lake-id="ue9047393" id="ue9047393">在getDefaultExecutor的实现中，并不是一上来就直接new SimpleAsyncTaskExecutor()的，而是先尝试着获取默认的执行器。</span></p>
  <p data-lake-id="u879f71a8" id="u879f71a8"><span data-lake-id="u09843345" id="u09843345">​</span><br></p>
  <pre lang="java"><code>
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
    Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
    return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}

</code></pre>
  <p data-lake-id="uafff793d" id="uafff793d"><br></p>
  <p data-lake-id="u0dad67bb" id="u0dad67bb"><span data-lake-id="ua82fc978" id="ua82fc978">看一下这个</span><code data-lake-id="u29ffee16" id="u29ffee16"><span data-lake-id="u46b0a428" id="u46b0a428">super.getDefaultExecutor(beanFactory);</span></code><span data-lake-id="u8cc631e9" id="u8cc631e9">代码：</span></p>
  <p data-lake-id="ue9535518" id="ue9535518"><span data-lake-id="u091fcb09" id="u091fcb09">​</span><br></p>
  <pre lang="java"><code>
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
    if (beanFactory != null) {
        try {
            // Search for TaskExecutor bean... not plain Executor since that would
            // match with ScheduledExecutorService as well, which is unusable for
            // our purposes here. TaskExecutor is more clearly designed for it.
            return beanFactory.getBean(TaskExecutor.class);
        }
        catch (NoUniqueBeanDefinitionException ex) {
            logger.debug("Could not find unique TaskExecutor bean. " +
                    "Continuing search for an Executor bean named 'taskExecutor'", ex);
            try {
                return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
            }
            catch (NoSuchBeanDefinitionException ex2) {
                if (logger.isInfoEnabled()) {
                    logger.info("More than one TaskExecutor bean found within the context, and none is named " +
                            "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " +
                            "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
                }
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
            logger.debug("Could not find default TaskExecutor bean. " +
                    "Continuing search for an Executor bean named 'taskExecutor'", ex);
            try {
                return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
            }
            catch (NoSuchBeanDefinitionException ex2) {
                logger.info("No task executor bean found for async processing: " +
                        "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
            }
            // Giving up -&gt; either using local default executor or none at all...
        }
    }
    return null;
}
</code></pre>
  <p data-lake-id="udca8f4c5" id="udca8f4c5"><br></p>
  <p data-lake-id="u74889a3c" id="u74889a3c"><span data-lake-id="uaf5ff9de" id="uaf5ff9de">简单总结一下，就是会先尝试获取TaskExecutor的实现类，这里如果能且仅能找到唯一一个，那么就用这个，如果找不到，或者找到了多个，那么就会走到</span><code data-lake-id="u71a9580b" id="u71a9580b"><span data-lake-id="u2ca20567" id="u2ca20567">catch (NoUniqueBeanDefinitionException ex) </span></code><span data-lake-id="u19e12aad" id="u19e12aad">和</span><code data-lake-id="ua9bed66d" id="ua9bed66d"><span data-lake-id="u9076cec7" id="u9076cec7">catch (NoSuchBeanDefinitionException ex)</span></code><span data-lake-id="uef22034e" id="uef22034e">的分支中，这里就是获取beanName为taskExecutor的Bean</span></p>
  <p data-lake-id="ud3a57beb" id="ud3a57beb"><span data-lake-id="ubcfb4a9e" id="ubcfb4a9e">​</span><br></p>
  <p data-lake-id="u3ac1988e" id="u3ac1988e"><span data-lake-id="ue232172f" id="ue232172f">以上查询如果还是没查到，那么再创建SimpleAsyncTaskExecutor。</span></p>
  <p data-lake-id="ua739ce9e" id="ua739ce9e"><span data-lake-id="ud559c2f7" id="ud559c2f7">​</span><br></p>
  <p data-lake-id="u3c2bde0b" id="u3c2bde0b"><strong><span data-lake-id="u822b9f89" id="u822b9f89">所以，也就是说Spring给了我们一个机会，他也知道SimpleAsyncTaskExecutor这玩意性能不行，所以他会尝试获取一个我们自定义的线程池，如果我们定义了一个，那么他就会用我们定义的这个，如果我们定义了多个，那么他就不用了。</span></strong></p>
  <p data-lake-id="u11239fb9" id="u11239fb9"><strong><span data-lake-id="u54ad78bd" id="u54ad78bd">​</span></strong><br></p>
  <p data-lake-id="uc8fe2d8c" id="uc8fe2d8c"><strong><span data-lake-id="u42438bfe" id="u42438bfe">所以，这也是为什么有的时候我们自定义了一个线程池，但是没有在@Async中指定，他也能用到他的原因，但是这种方式并不靠谱，万一后面又新定义了一个，那就凉凉了。或者我们可以把一个线程池的BeanName设置成taskExecutor也行。</span></strong></p>
 </body>
</html>